﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using uk.ac.soton.ses.DAL;
using uk.ac.soton.ses.ExperimentActionsWebPart;
using uk.ac.soton.ses.ExperimentCollectionsWebPart;
using uk.ac.soton.ses.ExperimentLinksWebPart;
using uk.ac.soton.ses.WebControls;
using uk.ac.soton.ses.UserControls;

namespace uk.ac.soton.ses.layouts
{
    /// <summary>
    /// SharePoint application web page that renders details of an experiment
    /// </summary>
    public partial class ExperimentDetails : LayoutsPageBase
    {
        private int _experimentID = -1;
        private Experiment _experiment = null;
        private ExperimentCollectionsWebPartUserControl _experimentCollectionsControl = null;
        private ExperimentParameterGridNew _experimentParameterGridNew = null;
        private ExperimentLinksWebPartUserControl _experimentLinksControl = null;
        private ExperimentDataItems _dataItemsControl;
        private ExperimentMetadataToolsUserControl _metadataToolsControl;
        ExperimentActionsWebPartUserControl _experimentActionsControl = null;
        private ExperimentQuickSearch _searchControl;
        private bool _isInitialised = false;
        private MaterialsMetadataContext _dbcontext = null;
        private WindowsIdentity _currentUser = null;

        private string js_eventsetup = @"
            $(window).load(function () {
                //Changed to $(window).load() as $(document).ready() was not working (conflicting with body onload---see jquery docs)
                $('#datafilestitle').live('click', function () { $('#datafiles').toggle(); });
                $('#parameterstitle').live('click', function () { $('#parameters').toggle(); });
                $('#linkingtitle').live('click', function () { $('#linking').toggle(); });
            });";


        #region Page life cycle methods

        /// <summary>
        /// OnInit in ASP.NET page life cycle overridden. We add controls here, load data and load HDC plugins.
        /// </summary>
        /// <param name="e">Event arguments received by events</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            try
            {
                this._dbcontext = HttpContext.Current.GetMaterialsDBContext();
                this._currentUser = Context.User.Identity as WindowsIdentity;
                this.LoadExperiment();
                this.AddControls();
                this.ConfigureEvents();

                //Allow Page_Load to databind
                this._isInitialised = true;
            }
            catch (ExperimentLoadException)
            {
                //If we didn't get an experiment, we shouldn't do a databind
                literal_ExperimentTitle.Text = String.Format("<h3 class=\"HDCH3 Error\">Error loading experiment {0}</h3>", Page.Request.QueryString["ID"]);
                //Set SharePoint's window title
                literal_PageTitle.Text = string.Format("Error loading experiment {0}", this.Page.Request.QueryString["ID"]);
                this.HideExperiment();
            }
            catch (ExperimentAccessDeniedException accessDeniedException)
            {
                literal_ExperimentTitle.Text = String.Format("<h3 class=\"HDCH3 Error\">{0}</h3>", accessDeniedException.Message);
                //Set SharePoint's window title
                literal_PageTitle.Text = string.Format("Access denied to experiment {0}", this.Page.Request.QueryString["ID"]);
                this.HideExperiment();
            }
            catch (Exception ex)
            {
                this.HandleException(ex);
            }
        }

        /// <summary>
        /// Page_Load in ASP.NET page life cycle. We databind controls here.
        /// </summary>
        /// <param name="sender">Usual sender object received by events</param>
        /// <param name="e">Event arguments received by events</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            this.PrintMessage("Page_Load.");

            //This is for debugging security information
            //this.DisplayDebuggingInfo();

            if (this._isInitialised)
            {

                if (!Page.IsPostBack)
                {

                    try
                    {
                        //Data bind on first page load, but not on postback
                        this.DataBindAllControls();
                    }
                    catch (Exception ex)
                    {
                        this.HandleException(ex);
                    }
                }
                else
                {
                    //Set the experiment ID
                    ExperimentActionsWebPartUserControl ctl_ExperimentActions = (ExperimentActionsWebPartUserControl)this.Control_ExperimentActions;
                    ctl_ExperimentActions.ExperimentID = this._experiment.ID;

                }
            }
        }

        /// <summary>
        /// Dispose overridden to make sure that the database context is disposed of correctly
        /// </summary>
        public override void Dispose()
        {
            base.Dispose();
            if (this._dbcontext != null)
            {
                this._dbcontext.Connection.Close();
                this._dbcontext.Dispose();
            }
        }

        #endregion

        #region Private helper methods

        /// <summary>
        /// Setup the event handlers for controls on the page
        /// </summary>
        private void ConfigureEvents()
        {
            this.View_ExperimentDetails.ModeChanging += new FormViewModeEventHandler(this.View_ExperimentDetails_ModeChanging);
            this.View_ExperimentDetails.ItemCommand += new FormViewCommandEventHandler(this.View_ExperimentDetails_ItemCommand);
            this.View_ExperimentDetails.ItemUpdating += new FormViewUpdateEventHandler(this.View_ExperimentDetails_ItemUpdating);
            this.View_ExperimentDetails.DataBound += new EventHandler(this.View_ExperimentDetails_DataBound);

            //Handle metadata rebinding requests from the data items control
            this._dataItemsControl.FullRebindRequired += new EventHandler(this.FullRebindRequired);
            this._metadataToolsControl.MetadataRebindRequired += new EventHandler(this.MetadataRebindRequired);
            this._searchControl.ExperimentSelected += new ExperimentSelectedEventHandler(this.QuickSearchControl_ExperimentSelected);
        }

        /// <summary>
        /// Add controls dynamically are not already in markup
        /// </summary>
        private void AddControls()
        {
            //Add stylesheet control
            this.Page.AddHDCStylesheet();
            this.Page.AddHDCJQuery(true);
            this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "js_eventsetup", js_eventsetup, true);

            //Configure SharePoint's page heading (not the window title or our proper title)
            literal_PageHeading.Text = string.Format("Experiment {0}", Page.Request.QueryString["ID"]);

            //Parameter entry
            this._experimentParameterGridNew = new ExperimentParameterGridNew(this._experimentID);
            this.ParameterEntryPlaceHolder.Controls.Add(this._experimentParameterGridNew);

            //Find the experiment actions control
            this._experimentActionsControl = this.Control_ExperimentActions as ExperimentActionsWebPartUserControl;
            this._experimentActionsControl.ExperimentID = this._experimentID;

            //Find the experiment collections control
            this._experimentCollectionsControl = this.Control_ExperimentCollections as ExperimentCollectionsWebPartUserControl;
            this._experimentCollectionsControl.ExperimentID = this._experimentID;

            //Find the experiment links control
            this._experimentLinksControl = this.Control_ExperimentLinks as ExperimentLinksWebPartUserControl;
            this._experimentLinksControl.ExperimentID = this._experimentID;

            //Find the data items UserControl, cast it, and store it in a field
            this._dataItemsControl = this.Control_ExperimentDataItems as ExperimentDataItems;

            //Find the experiments quick search box, cast it, and store it in a field
            this._searchControl = this.Control_ExperimentQuickSearch as ExperimentQuickSearch;

            //Find the metadata tools control
            this._metadataToolsControl = this.Control_ExperimentMetadataTools as ExperimentMetadataToolsUserControl;
        }

        /// <summary>
        /// Load the experiment into memory, or throw exceptions
        /// </summary>
        /// <exception cref="ExperimentAccessDeniedException">When user does not have permission to the experiment</exception>
        /// <exception cref="ExperimentLoadException">When the experiment can't be found or a valid ID was not passed</exception>
        private void LoadExperiment()
        {
            string idString = Page.Request.QueryString["ID"];
            if (int.TryParse(idString, out this._experimentID))
            {
                //Load Experiment from database as it may be used elsewhere
                Experiment experiment = this._dbcontext.GetExperimentByID(this._experimentID);
                if (experiment != null)
                {
                    //The following line is very critical as we could grant permission to the wrong experiment if HasPermission() returns the wrong data or we handle it incorrectly
                    if (experiment.HasPermission(this._currentUser, this._dbcontext))
                    {
                        //We should be okay with this experiment so store it in a variable for use elsewhere
                        this._experiment = experiment;
                        this._experiment.ExperimentTypeReference.Load();
                    }
                    else
                    {
                        throw new ExperimentAccessDeniedException(String.Format("Access denied to experiment {0} ({1}). Owner is {2}, email {3}", experiment.ID, experiment.Name, experiment.User.Name, experiment.User.Email));
                    }
                }
                else
                {
                    throw new ExperimentLoadException("Cannot load experiment");
                }
            }
            else
            {
                throw new ExperimentLoadException("Cannot load experiment");
            }
        }


        /// <summary>
        /// Reports exceptions by adding a new label to top of page
        /// </summary>
        /// <param name="e">The exception to report</param>
        private void HandleException(Exception e)
        {
            HDCSharedWebFunctions.AddErrorLabel("An error occurred: " + e.Message, this.placeholder_ErrorReporting);
            this.HideExperiment();
            this._isInitialised = false;
        }

        /// <summary>
        /// Hides the experiment when we can't load it properly
        /// </summary>
        private void HideExperiment()
        {
            this.panel_Experiment.Visible = false;
        }

        private void SetBackground()
        {
            if (this._experiment.ExperimentType.ID == (int)HDCExperimentTypes.Template)
            {
                if (!this.panel_Experiment.CssClass.Contains("HDCExperimentTemplate"))
                {
                    this.panel_Experiment.CssClass += " HDCExperimentTemplate";
                }
            }
            else
            {
                this.panel_Experiment.CssClass = this.panel_Experiment.CssClass.Replace("HDCExperimentTemplate", "");
            }
        }

        private void SetThumbnail()
        {
            string thumbdatafileurl = this._experiment.GetThumbnail(this._dbcontext);
            if (thumbdatafileurl != null)
            {
                this.Image_ExperimentThumbnail.ImageUrl = thumbdatafileurl;
                this.Image_ExperimentThumbnail.Visible = true;
            }
            else
            {
                this.Image_ExperimentThumbnail.ImageUrl = "";
                this.Image_ExperimentThumbnail.Visible = false;
            }
        }

        /// <summary>
        /// Some controls demand we rebind the metadata grid. This function handles this event.
        /// </summary>
        /// <param name="sender">The control that requested the rebind</param>
        /// <param name="e">Standard EventArgs object</param>
        private void MetadataRebindRequired(object sender, EventArgs e)
        {
            this._experimentParameterGridNew.Recreate();
        }

        /// <summary>
        /// Some controls demand we rebind everything. This function handles this event.
        /// </summary>
        /// <param name="sender">The control that requested the rebind</param>
        /// <param name="e">Standard EventArgs object</param>
        private void FullRebindRequired(object sender, EventArgs e)
        {
            this.RebindAllControls();
        }

        private void QuickSearchControl_ExperimentSelected(object sender, ExperimentSelectedEventArgs e)
        {
            Response.Redirect(HDCConfiguration.GetUrl_ExperimentDetails(e.SelectedExperimentID));
        }

        #endregion

        #region Data binding

        /// <summary>
        /// Parent data bind method that calls data binding on all required controls
        /// </summary>
        private void DataBindAllControls()
        {
            this.PrintMessage("DataBindAllControls");

            //Only bind if the experiment exists
            if (this._experiment!=null)
            {
                //The background is set to a different colour when the experiment is a template
                this.SetBackground();

                //Set the thumbnail URL
                this.SetThumbnail();

                //Bind the experiment details view
                this.DataBindExperimentDetails();

                //Bind the related experiments
                this.DataBindRelatedExperiments();

                //Bind the experiment collections control
                //this._experimentCollectionsControl.BindToExperiment(this._experimentID);

                //Bind the experiment links control
                //this._experimentLinksControl.BindToExperiment(this._experimentID);
            }
        }

        private void RebindAllControls()
        {
            this.DataBindAllControls();
            this._metadataToolsControl.DataBindAllControls();
        }

        /// <summary>
        /// Data binding experiment details
        /// </summary>
        private void DataBindExperimentDetails()
        {
            this.View_ExperimentDetails.DataSource = new List<Experiment> { this._experiment };
            this.View_ExperimentDetails.DataBind();

            //Set title
            literal_ExperimentTitle.Text = (string.Format("<h3 class=\"HDCH3\">Experiment &quot;{0}&quot;: {1}</h3>", this._experiment.Name, this._experiment.Description ?? "No description"));
            //Set SharePoint's window title
            literal_PageTitle.Text = string.Format("Experiment {0}&mdash;&quot;{1}&quot;", this._experiment.ID, this._experiment.Name);
        }


        /// <summary>
        /// Data bind the related experiment lists
        /// </summary>
        private void DataBindRelatedExperiments()
        {
            //Find experiments that are related in some way
            this.list_RelatedExperiments_Linked.DataSource = this._dbcontext.GetRelatedExperiments(this._experimentID, 10, true);
            this.list_RelatedExperiments_Linked.DataBind();

            //Find experiments of same type and bind them
            this.list_RelatedExperiments_SameType.DataSource = this._dbcontext.GetExperimentsOfSameType(this._experimentID, 10, true);
            this.list_RelatedExperiments_SameType.DataBind();
        }

        #endregion

        #region View_ExperimentDetails Events

        private void View_ExperimentDetails_ItemUpdating(object sender, FormViewUpdateEventArgs e)
        {
            TextBox textbox_description = (TextBox)this.View_ExperimentDetails.FindControl("TextBox_Description");
            DropDownList ddl_types = (DropDownList)this.View_ExperimentDetails.FindControl("List_ExperimentTypes");
            if (textbox_description != null && ddl_types != null)
            {
                int selectedExperimentType = 0;
                if (int.TryParse(ddl_types.SelectedValue, out selectedExperimentType))
                {
                    this._dbcontext.UpdateExperiment(this._experimentID, textbox_description.Text, selectedExperimentType);
                    this._dbcontext.SaveChanges();

                    this.View_ExperimentDetails.ChangeMode(FormViewMode.ReadOnly);
                    this.RebindAllControls();
                }
            }
        }

        private void View_ExperimentDetails_ItemCommand(object sender, FormViewCommandEventArgs e)
        {
            switch (e.CommandName)
            {
                case "Save":
                    this.View_ExperimentDetails.UpdateItem(true);
                    break;
            }
        }

        private void View_ExperimentDetails_ModeChanging(object sender, FormViewModeEventArgs e)
        {
            this.View_ExperimentDetails.ChangeMode(e.NewMode);
            this.DataBindExperimentDetails();
        }

        private void View_ExperimentDetails_DataBound(object sender, EventArgs e)
        {
            if (this.View_ExperimentDetails.CurrentMode == FormViewMode.ReadOnly)
            {
                this.DataBindExperimentDetailsReadOnlyMode();
            }
            else
            {
                this.DataBindExperimentDetailsModifyMode();
            }
        }

        /// <summary>
        /// Populates the controls in the read only mode of the experiment details view
        /// </summary>
        private void DataBindExperimentDetailsReadOnlyMode()
        {
            //Find controls
            Label lbl_id = (Label)this.View_ExperimentDetails.FindControl("Label_ID");
            Label lbl_name = (Label)this.View_ExperimentDetails.FindControl("Label_Name");
            LinkButton btn_type = (LinkButton)this.View_ExperimentDetails.FindControl("Button_Type");
            Label lbl_date = (Label)this.View_ExperimentDetails.FindControl("Label_Date");
            Label lbl_descriptiontitle = (Label)this.View_ExperimentDetails.FindControl("Label_DescriptionTitle");
            LinkButton btn_description = (LinkButton)this.View_ExperimentDetails.FindControl("Button_Description");
            Label lbl_locationtitle = (Label)this.View_ExperimentDetails.FindControl("Label_LocationTitle");
            Label lbl_location = (Label)this.View_ExperimentDetails.FindControl("Label_Location");

            //Populate controls
            lbl_id.Text = this._experiment.ID.ToString();
            lbl_name.Text = this._experiment.Name;
            btn_type.Text = this._experiment.ExperimentType.Name;
            lbl_date.Text = this._experiment.Date.ToString();

            //Populate location information if we have one
            if (this._experiment.IsDeleted)
            {
                lbl_locationtitle.Text = "No matching data file folder";
                lbl_location.Text = "";
                lbl_location.Visible = false;
            }
            else
            {
                lbl_locationtitle.Text = "Files stored in ";
                lbl_location.Text = this._experiment.GetUNCPath(this._dbcontext) ?? this._experiment.DefaultBasePath;
                lbl_location.Font.Bold = true;
            }

            //Populate description if we have one
            if (string.IsNullOrEmpty(this._experiment.Description))
            {
                lbl_descriptiontitle.Visible = false;
                lbl_descriptiontitle.Text = "";
                btn_description.Text = "No description";
            }
            else
            {
                lbl_descriptiontitle.Text = "With description ";
                btn_description.Text = this._experiment.Description;
            }
        }

        /// <summary>
        /// Populates the controls in the modify mode of the experiment details view
        /// </summary>
        private void DataBindExperimentDetailsModifyMode()
        {
            //Find the controls
            TextBox description = (TextBox)this.View_ExperimentDetails.FindControl("TextBox_Description");
            DropDownList ddl_type = (DropDownList)this.View_ExperimentDetails.FindControl("List_ExperimentTypes");

            //Populate the description text box
            description.Text = this._experiment.Description;

            //Populate the drop down list
            IEnumerable<ExperimentType> experimentTypes = this._dbcontext.ExperimentTypes;
            ddl_type.DataSource = experimentTypes;
            ddl_type.DataBind();
            ddl_type.Items.FindByValue(this._experiment.ExperimentType.ID.ToString()).Selected = true;
        }

        #endregion

        #region Debugging methods

        /// <summary>
        /// Print a message to the page (debugging method, may need tweaking before using)
        /// </summary>
        /// <param name="message">The string to print</param>
        private void PrintMessage(string message)
        {
            //When debugging, this will need to be put back in
            //error_output.Text = error_output.Text + Environment.NewLine + message;
        }

        /// <summary>
        /// Print various debugging information such as user identity (may need tweaking before using)
        /// </summary>
        private void DisplayDebuggingInfo()
        {
            //This is for debugging security information
            TextBox tb = new TextBox();
            tb.Rows = 7;
            tb.TextMode = TextBoxMode.MultiLine;
            tb.Width = 750;
            tb.ReadOnly = true;
            tb.BorderStyle = BorderStyle.Solid;
            tb.BackColor = System.Drawing.Color.Aqua;
            var winIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
            var htUser = System.Web.HttpContext.Current.User;
            var th = System.Threading.Thread.CurrentPrincipal;

            //Label lbl = AddErrorLabel(Context.User.Identity.Name, panel_Experiment);
            //lbl.Text += Environment.NewLine + SPContext.Current.Web.CurrentUser.LoginName;
            //lbl.Text += Environment.NewLine + System.Security.Principal.WindowsIdentity.GetCurrent().Name;
            //lbl.Text += Environment.NewLine + SPContext.Current.Web.CurrentUser.Sid;

            tb.Text += "WindowsIdentity.GetCurrent(): " + winIdentity.Name + Environment.NewLine;
            tb.Text += "HttpContext.Current.User: " + htUser.Identity.Name + Environment.NewLine;
            tb.Text += "Thread.CurrentPrincipal: " + th.Identity.Name + Environment.NewLine;

            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                var winIdentity2 = System.Security.Principal.WindowsIdentity.GetCurrent();
                var htUser2 = System.Web.HttpContext.Current.User;
                var th2 = System.Threading.Thread.CurrentPrincipal;
                tb.Text += "Elevated Privileges: " + Environment.NewLine;
                tb.Text += "WindowsIdentity.GetCurrent(): " + winIdentity2.Name + Environment.NewLine;
                tb.Text += "HttpContext.Current.User: " + htUser2.Identity.Name + Environment.NewLine;
                tb.Text += "Thread.CurrentPrincipal: " + th2.Identity.Name + Environment.NewLine;
            });
            this.DebuggingInfoPlaceHolder.Controls.Add(tb);
        }

        #endregion

    }
}


//Just playing, but this code may be useful later:
//SPWeb site = Microsoft.SharePoint.WebControls.SPControl.GetContextWeb(Context);
//SPWeb site = SPContext.Current.Web;
//SPDataSource spDataSource = new SPDataSource(site.Lists["ExternalDataFiles_SQL"]);
//this.Controls.Add(spDataSource);
//spDataSource.DataSourceMode = SPDataSourceMode.List;
//spDataSource.IncludeHidden = false;
//spDataSource.Scope = SPViewScope.Recursive;
//SPWeb site = SPContext.Current.Web;
//SPDataSource dataSource = new SPDataSource(site.Lists["ExternalDataFiles_SQL"]);
//dataSource.DataSourceMode = SPDataSourceMode.List;
//dataSource.SelectParameters.Add(new System.Web.UI.WebControls.Parameter(
//dataFilesDataSourcePlaceHolder.Controls.Add(dataSource);
//gvExperimentDataItems.DataSource = dataSource;
//gvExperimentDataItems.DataBind();